sysroot: Support more arbitrary deployment changes
authorColin Walters <walters@verbum.org>
Tue, 17 Sep 2013 02:37:00 +0000 (22:37 -0400)
committerColin Walters <walters@verbum.org>
Thu, 3 Oct 2013 00:18:06 +0000 (20:18 -0400)
This commit changes the sysroot API so that one can create arbitrary
new deployment checkouts, then commit them as one step.  This is to
enable things like an automatic bisection tool which say create 50
deployments at once, then when done clean them up.

This also moves some printfs from the library into src/ostree.

src/libostree/ostree-sysroot-cleanup.c
src/libostree/ostree-sysroot-deploy.c
src/libostree/ostree-sysroot-private.h
src/libostree/ostree-sysroot.c
src/libostree/ostree-sysroot.h
src/ostree/ot-admin-builtin-deploy.c
src/ostree/ot-admin-builtin-upgrade.c
src/ostree/ot-admin-functions.c
src/ostree/ot-admin-functions.h
tests/test-admin-deploy-1.sh

index 186c6cb25dbffa10fc116eb22c4c0dea0c1745ab..884597b2343f73d5ddd688efa750d51b27ea98d3 100644 (file)
 
 #include "ostree-sysroot-private.h"
 
-static gboolean
-list_deployment_dirs_for_os (GFile               *osdir,
-                             GPtrArray           *inout_deployments,
-                             GCancellable        *cancellable,
-                             GError             **error)
+gboolean
+_ostree_sysroot_list_deployment_dirs_for_os (GFile               *osdir,
+                                             GPtrArray           *inout_deployments,
+                                             GCancellable        *cancellable,
+                                             GError             **error)
 {
   gboolean ret = FALSE;
   const char *osname = gs_file_get_basename_cached (osdir);
@@ -136,7 +136,8 @@ list_all_deployment_directories (OstreeSysroot       *self,
       if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
         continue;
       
-      if (!list_deployment_dirs_for_os (child, ret_deployments, cancellable, error))
+      if (!_ostree_sysroot_list_deployment_dirs_for_os (child, ret_deployments,
+                                                        cancellable, error))
         goto out;
     }
   
@@ -340,7 +341,6 @@ cleanup_old_deployments (OstreeSysroot       *self,
           if (device == root_device && inode == root_inode)
             continue;
 
-          g_print ("ostadmin: Deleting deployment %s\n", gs_file_get_path_cached (deployment_path));
           if (!gs_shutil_rm_rf (deployment_path, cancellable, error))
             goto out;
           if (!gs_shutil_rm_rf (origin_path, cancellable, error))
@@ -365,7 +365,6 @@ cleanup_old_deployments (OstreeSysroot       *self,
       if (g_hash_table_lookup (active_boot_checksums, bootcsum))
         continue;
 
-      g_print ("ostadmin: Deleting bootdir %s\n", gs_file_get_path_cached (bootdir));
       if (!gs_shutil_rm_rf (bootdir, cancellable, error))
         goto out;
     }
index 14136d2462025b6f175f823454c870ffbb95c1e8..229bf1514f923375f431960c0192917a98474cc3 100644 (file)
@@ -459,224 +459,6 @@ checksum_from_kernel_src (GFile        *src,
   return TRUE;
 }
 
-static int
-sort_by_bootserial (gconstpointer ap, gconstpointer bp)
-{
-  OstreeDeployment **a_loc = (OstreeDeployment**)ap;
-  OstreeDeployment *a = *a_loc;
-  OstreeDeployment **b_loc = (OstreeDeployment**)bp;
-  OstreeDeployment *b = *b_loc;
-
-  if (ostree_deployment_get_bootserial (a) == ostree_deployment_get_bootserial (b))
-    return 0;
-  else if (ostree_deployment_get_bootserial (a) < ostree_deployment_get_bootserial (b))
-    return -1;
-  return 1;
-}
-
-static GPtrArray *
-filter_deployments_by_bootcsum (GPtrArray    *deployments,
-                                const char   *osname,
-                                const char   *bootcsum)
-{
-  GPtrArray *ret = g_ptr_array_new ();
-  guint i;
-
-  for (i = 0; i < deployments->len; i++)
-    {
-      OstreeDeployment *deployment = deployments->pdata[i];
-      
-      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-        continue;
-      if (strcmp (ostree_deployment_get_bootcsum (deployment), bootcsum) != 0)
-        continue;
-      
-      g_ptr_array_add (ret, deployment);
-    }
-  g_ptr_array_sort (ret, sort_by_bootserial);
-
-  return ret;
-}
-
-static void
-compute_new_deployment_list (int           current_bootversion,
-                             GPtrArray    *current_deployments,
-                             const char   *osname,
-                             OstreeDeployment *booted_deployment,
-                             OstreeDeployment *merge_deployment,
-                             gboolean      retain,
-                             const char   *revision,
-                             const char   *bootcsum,
-                             GPtrArray   **out_new_deployments)
-{
-  guint i;
-  int new_index;
-  guint new_deployserial = 0;
-  int new_bootserial = 0;
-  gs_unref_object OstreeDeployment *new_deployment = NULL;
-  gs_unref_ptrarray GPtrArray *matching_deployments_by_bootserial = NULL;
-  OstreeDeployment *deployment_to_delete = NULL;
-  gs_unref_ptrarray GPtrArray *ret_new_deployments = NULL;
-
-  /* First, compute the serial for this deployment; we look
-   * for other ones in this os with the same checksum.
-   */
-  for (i = 0; i < current_deployments->len; i++)
-    {
-      OstreeDeployment *deployment = current_deployments->pdata[i];
-      
-      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-        continue;
-      if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0)
-        continue;
-
-      new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1);
-    }
-
-  /* We retain by default (well, hardcoded now) one previous
-   * deployment for this OS, plus the booted deployment.  Usually, we
-   * have one previous, one into which we're booted, and we're
-   * deploying a new one.  So the old previous will get swapped out,
-   * and booted becomes previous.
-   *
-   * But if the user then upgrades again, we will end up pruning the
-   * front of the deployment list.  We never delete the running
-   * deployment.
-   */
-  if (!retain)
-    {
-      for (i = 0; i < current_deployments->len; i++)
-        {
-          OstreeDeployment *deployment = current_deployments->pdata[i];
-      
-          if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-            continue;
-
-          // Keep both the booted and merge deployments
-          if (ostree_deployment_equal (deployment, booted_deployment) || 
-              ostree_deployment_equal (deployment, merge_deployment))
-            continue;
-
-          deployment_to_delete = deployment;
-        }
-    }
-
-  ret_new_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
-
-  new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial,
-                                      bootcsum, new_bootserial);
-  g_ptr_array_add (ret_new_deployments, g_object_ref (new_deployment));
-  new_index = 1;
-  for (i = 0; i < current_deployments->len; i++)
-    {
-      OstreeDeployment *orig_deployment = current_deployments->pdata[i];
-      gs_unref_object OstreeDeployment *deployment_clone = NULL;
-
-      if (orig_deployment == deployment_to_delete)
-        continue;
-
-      deployment_clone = ostree_deployment_clone (orig_deployment);
-      ostree_deployment_set_index (deployment_clone, new_index);
-      new_index++;
-      g_ptr_array_add (ret_new_deployments, g_object_ref (deployment_clone));
-    }
-
-  /* Just renumber the deployments for the OS we're adding; we don't
-   * handle anything else at the moment.
-   */
-  matching_deployments_by_bootserial = filter_deployments_by_bootcsum (ret_new_deployments,
-                                                                       osname, bootcsum);
-  for (i = 0; i < matching_deployments_by_bootserial->len; i++)
-    {
-      OstreeDeployment *deployment = matching_deployments_by_bootserial->pdata[i];
-      ostree_deployment_set_bootserial (deployment, i);
-    }
-
-  *out_new_deployments = ret_new_deployments;
-  ret_new_deployments = NULL;
-}
-
-static GHashTable *
-object_array_to_set (GPtrArray   *objlist,
-                     GHashFunc    hashfunc,
-                     GEqualFunc   equalfunc)
-{
-  GHashTable *ret = g_hash_table_new_full (hashfunc, equalfunc, g_object_unref, NULL);
-  guint i;
-
-  for (i = 0; i < objlist->len; i++)
-    {
-      GObject *obj = g_object_ref (objlist->pdata[i]);
-      g_hash_table_insert (ret, obj, obj);
-    }
-  
-  return ret;
-}
-
-static GHashTable *
-object_set_subtract (GHashTable *a, GHashTable *b)
-{
-  GHashTable *ret = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
-  GHashTableIter hashiter;
-  gpointer hashkey, hashvalue;
-  
-  g_hash_table_iter_init (&hashiter, a);
-  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
-    {
-      if (!g_hash_table_contains (b, hashkey))
-        {
-          GObject *o = g_object_ref (hashkey);
-          g_hash_table_insert (ret, o, o);
-        }
-    }
-
-  return ret;
-}
-
-static void
-print_deployment_set (gboolean    for_removal,
-                      GHashTable *set)
-{
-  GHashTableIter hashiter;
-  gpointer hashkey, hashvalue;
-
-  if (g_hash_table_size (set) == 0)
-    return;
-
-  g_print ("%s\n", for_removal ? "removed:" : "added: ");
-
-  g_hash_table_iter_init (&hashiter, set);
-  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
-    {
-      OstreeDeployment *deployment = hashkey;
-
-      g_print ("  %c %s %s.%d",
-               for_removal ? '-' : '+', ostree_deployment_get_osname (deployment),
-               ostree_deployment_get_csum (deployment),
-               ostree_deployment_get_deployserial (deployment));
-
-      if (!for_removal)
-        g_print (" index=%d", ostree_deployment_get_index (deployment));
-      g_print ("\n");
-    }
-}
-
-static void
-print_deployment_diff (GPtrArray   *current_deployments,
-                       GPtrArray   *new_deployments)
-{
-  gs_unref_hashtable GHashTable *curset = object_array_to_set (current_deployments, ostree_deployment_hash, ostree_deployment_equal);
-  gs_unref_hashtable GHashTable *newset = object_array_to_set (new_deployments, ostree_deployment_hash, ostree_deployment_equal);
-  gs_unref_hashtable GHashTable *removed = NULL;
-  gs_unref_hashtable GHashTable *added = NULL;
-
-  removed = object_set_subtract (curset, newset);
-  added = object_set_subtract (newset, curset);
-
-  print_deployment_set (TRUE, removed);
-  print_deployment_set (FALSE, added);
-}
-
 /* FIXME: We should really do individual fdatasync() on files/dirs,
  * since this causes us to block on unrelated I/O.  However, it's just
  * safer for now.
@@ -970,7 +752,8 @@ swap_bootloader (OstreeSysroot  *sysroot,
 }
 
 static GHashTable *
-bootcsum_counts_for_deployment_list (GPtrArray   *deployments)
+bootcsum_counts_for_deployment_list (GPtrArray   *deployments,
+                                     gboolean     set_bootserial)
 {
   guint i;
   GHashTable *ret = 
@@ -984,7 +767,52 @@ bootcsum_counts_for_deployment_list (GPtrArray   *deployments)
 
       count = GPOINTER_TO_UINT (g_hash_table_lookup (ret, bootcsum));
       g_hash_table_replace (ret, (char*)bootcsum, GUINT_TO_POINTER (count + 1));
+
+      if (set_bootserial)
+        ostree_deployment_set_bootserial (deployment, count);
+    }
+  return ret;
+}
+
+/* TEMPORARY HACK: Add a "current" symbolic link that's easy to
+ * follow inside the gnome-ostree build scripts.  This isn't atomic,
+ * but that doesn't matter because it's only used by deployments
+ * done from the host.
+ */
+static gboolean
+create_current_symlinks (OstreeSysroot         *self,
+                         GCancellable          *cancellable,
+                         GError               **error)
+{
+  gboolean ret = FALSE;
+  guint i;
+  gs_unref_hashtable GHashTable *created_current_for_osname =
+    g_hash_table_new (g_str_hash, g_str_equal);
+
+  for (i = 0; i < self->deployments->len; i++)
+    {
+      OstreeDeployment *deployment = self->deployments->pdata[i];
+      const char *osname = ostree_deployment_get_osname (deployment);
+
+      if (!g_hash_table_lookup (created_current_for_osname, osname))
+        {
+          gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (self->path, "ostree/deploy/%s", osname);
+          gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current");
+          gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (self, deployment);
+          gs_free char *target = g_file_get_relative_path (osdir, deployment_path);
+          
+          g_assert (target != NULL);
+          
+          if (!ot_gfile_atomic_symlink_swap (os_current_path, target,
+                                             cancellable, error))
+            goto out;
+
+          g_hash_table_insert (created_current_for_osname, (char*)osname, GUINT_TO_POINTER (1));
+        }
     }
+
+  ret = TRUE;
+ out:
   return ret;
 }
 
@@ -1007,9 +835,16 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
   gboolean ret = FALSE;
   guint i;
   gboolean requires_new_bootversion = FALSE;
+  gboolean found_booted_deployment = FALSE;
+  gs_unref_hashtable GHashTable *new_bootcsum_to_count = NULL;
 
   g_assert (self->loaded);
 
+  /* Calculate the total number of deployments per bootcsums; while we
+   * are doing this, assign a bootserial to each new deployment.
+   */
+  new_bootcsum_to_count = bootcsum_counts_for_deployment_list (new_deployments, TRUE);
+
   /* Determine whether or not we need to touch the bootloader
    * configuration.  If we have an equal number of deployments and
    * more strongly an equal number of deployments per bootcsum, then
@@ -1022,9 +857,7 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
       GHashTableIter hashiter;
       gpointer hkey, hvalue;
       gs_unref_hashtable GHashTable *orig_bootcsum_to_count
-        = bootcsum_counts_for_deployment_list (self->deployments);
-      gs_unref_hashtable GHashTable *new_bootcsum_to_count
-        = bootcsum_counts_for_deployment_list (new_deployments);
+        = bootcsum_counts_for_deployment_list (self->deployments, FALSE);
 
       g_hash_table_iter_init (&hashiter, orig_bootcsum_to_count);
       while (g_hash_table_iter_next (&hashiter, &hkey, &hvalue))
@@ -1041,6 +874,22 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
         }
     }
 
+  for (i = 0; i < new_deployments->len; i++)
+    {
+      OstreeDeployment *deployment = new_deployments->pdata[i];
+      
+      if (deployment == self->booted_deployment)
+        found_booted_deployment = TRUE;
+
+      ostree_deployment_set_index (deployment, i);
+    }
+
+  if (self->booted_deployment && !found_booted_deployment)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Attempting to remove booted deployment");
+      goto out;
+    }
+
   if (!requires_new_bootversion)
     {
       if (!full_system_sync (cancellable, error))
@@ -1108,6 +957,8 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
         }
     }
 
+  g_print ("Transaction complete, performing cleanup\n");
+
   /* Now reload from disk */
   if (!ostree_sysroot_load (self, cancellable, error))
     {
@@ -1115,7 +966,57 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
       goto out;
     }
 
+  if (!create_current_symlinks (self, cancellable, error))
+    goto out;
+
+  /* And finally, cleanup of any leftover data.
+   */
+  if (!ostree_sysroot_cleanup (self, cancellable, error))
+    {
+      g_prefix_error (error, "Performing final cleanup: ");
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+allocate_deployserial (OstreeSysroot           *self,
+                       const char              *osname,
+                       const char              *revision,
+                       int                     *out_deployserial,
+                       GCancellable            *cancellable,
+                       GError                 **error)
+{
+  gboolean ret = FALSE;
+  guint i;
+  int new_deployserial = 0;
+  gs_unref_object GFile *osdir = NULL;
+  gs_unref_ptrarray GPtrArray *tmp_current_deployments =
+    g_ptr_array_new_with_free_func (g_object_unref);
+
+  osdir = ot_gfile_get_child_build_path (self->path, "ostree/deploy", osname, NULL);
+  
+  if (!_ostree_sysroot_list_deployment_dirs_for_os (osdir, tmp_current_deployments,
+                                                    cancellable, error))
+    goto out;
+
+  for (i = 0; i < tmp_current_deployments->len; i++)
+    {
+      OstreeDeployment *deployment = tmp_current_deployments->pdata[i];
+      
+      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
+        continue;
+      if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0)
+        continue;
+
+      new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1);
+    }
+
   ret = TRUE;
+  *out_deployserial = new_deployserial;
  out:
   return ret;
 }
@@ -1127,14 +1028,13 @@ ostree_sysroot_write_deployments (OstreeSysroot     *self,
  * @revision: Checksum to add
  * @origin: (allow-none): Origin to use for upgrades
  * @add_kernel_argv: (allow-none): Append these arguments to kernel configuration
- * @retain: If %TRUE, then do not delete earlier deployment
  * @provided_merge_deployment: (allow-none): Use this deployment for merge path
  * @out_new_deployment: (out): The new deployment path
  * @cancellable: Cancellable
  * @error: Error
  *
- * Add a new deployment with revision @revision; if @retain is %FALSE,
- * then an earlier deployment will be garbage collected.
+ * Check out deployment tree with revision @revision, performing a 3
+ * way merge with @provided_merge_deployment for configuration.
  */
 gboolean
 ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
@@ -1142,14 +1042,14 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
                                 const char        *revision,
                                 GKeyFile          *origin,
                                 char             **add_kernel_argv,
-                                gboolean           retain,
                                 OstreeDeployment  *provided_merge_deployment,
                                 OstreeDeployment **out_new_deployment,
                                 GCancellable      *cancellable,
                                 GError           **error)
 {
   gboolean ret = FALSE;
-  OstreeDeployment *new_deployment;
+  gint new_deployserial;
+  gs_unref_object OstreeDeployment *new_deployment = NULL;
   gs_unref_object OstreeDeployment *merge_deployment = NULL;
   gs_unref_object OstreeRepo *repo = NULL;
   gs_unref_object GFile *commit_root = NULL;
@@ -1158,7 +1058,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
   gs_unref_object GFile *new_deployment_path = NULL;
   gs_free char *new_bootcsum = NULL;
   gs_unref_object OstreeBootconfigParser *bootconfig = NULL;
-  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
 
   g_return_val_if_fail (osname != NULL || self->booted_deployment != NULL, FALSE);
 
@@ -1168,19 +1067,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
   if (!ostree_sysroot_get_repo (self, &repo, cancellable, error))
     goto out;
 
-  /* Here we perform cleanup of any leftover data from previous
-   * partial failures.  This avoids having to call gs_shutil_rm_rf()
-   * at random points throughout the process.
-   *
-   * TODO: Add /ostree/transaction file, and only do this cleanup if
-   * we find it.
-   */
-  if (!ostree_sysroot_cleanup (self, cancellable, error))
-    {
-      g_prefix_error (error, "Performing initial cleanup: ");
-      goto out;
-    }
-
   if (!ostree_repo_read_commit (repo, revision, &commit_root, NULL, cancellable, error))
     goto out;
 
@@ -1199,25 +1085,16 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
         goto out;
     }
 
-  /* If we're booted into the OS into which we're deploying, then
-   * merge the currently *booted* configuration, rather than the most
-   * recently deployed.
-   */
   if (provided_merge_deployment != NULL)
     merge_deployment = g_object_ref (provided_merge_deployment);
-  else
-    merge_deployment = ostree_sysroot_get_merge_deployment (self, osname);
-
-  compute_new_deployment_list (self->bootversion,
-                               self->deployments, osname,
-                               self->booted_deployment, merge_deployment,
-                               retain,
-                               revision, new_bootcsum,
-                               &new_deployments);
-  new_deployment = g_object_ref (new_deployments->pdata[0]);
-  ostree_deployment_set_origin (new_deployment, origin);
 
-  print_deployment_diff (self->deployments, new_deployments);
+  if (!allocate_deployserial (self, osname, revision, &new_deployserial,
+                              cancellable, error))
+    goto out;
+
+  new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial,
+                                          new_bootcsum, -1);
+  ostree_deployment_set_origin (new_deployment, origin);
 
   /* Check out the userspace tree onto the filesystem */
   if (!checkout_deployment_tree (self, repo, new_deployment, &new_deployment_path,
@@ -1274,34 +1151,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
       ostree_bootconfig_parser_set (bootconfig, "options", new_options);
     }
 
-  if (!ostree_sysroot_write_deployments (self, new_deployments, cancellable, error))
-    goto out;
-
-  g_print ("Transaction complete, performing cleanup\n");
-
-  /* TEMPORARY HACK: Add a "current" symbolic link that's easy to
-   * follow inside the gnome-ostree build scripts.  This isn't atomic,
-   * but that doesn't matter because it's only used by deployments
-   * done from the host.
-   */
-  {
-    gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (self->path, "ostree/deploy/%s", ostree_deployment_get_osname (new_deployment));
-    gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current");
-    gs_free char *target = g_file_get_relative_path (osdir, new_deployment_path);
-    g_assert (target != NULL);
-    if (!ot_gfile_atomic_symlink_swap (os_current_path, target,
-                                       cancellable, error))
-      goto out;
-  }
-
-  /* And finally, cleanup of any leftover data.
-   */
-  if (!ostree_sysroot_cleanup (self, cancellable, error))
-    {
-      g_prefix_error (error, "Performing final cleanup: ");
-      goto out;
-    }
-
   ret = TRUE;
   ot_transfer_out_value (out_new_deployment, &new_deployment);
  out:
index 864d3cd8e2ee06d096b630dc8c99c0a21ed8e568..ce79fb8e9ee66ca7886c443009e6c43b7775ca38 100644 (file)
@@ -60,6 +60,12 @@ _ostree_sysroot_parse_deploy_path_name (const char *name,
                                         int        *out_serial,
                                         GError    **error);
 
+gboolean
+_ostree_sysroot_list_deployment_dirs_for_os (GFile               *osdir,
+                                             GPtrArray           *inout_deployments,
+                                             GCancellable        *cancellable,
+                                             GError             **error);
+
 gboolean
 _ostree_sysroot_get_devino (GFile         *path,
                             guint32       *out_device,
index bf064d5ad9dcf7d784051af8dd16ce05010d0210..92d7b03570449e9f415d616d2a94d6ccdc7d0d81 100644 (file)
@@ -1070,6 +1070,10 @@ ostree_sysroot_get_merge_deployment (OstreeSysroot     *self,
   if (osname == NULL)
     osname = ostree_deployment_get_osname (self->booted_deployment);
 
+  /* If we're booted into the OS into which we're deploying, then
+   * merge the currently *booted* configuration, rather than the most
+   * recently deployed.
+   */
   if (self->booted_deployment &&
       g_strcmp0 (ostree_deployment_get_osname (self->booted_deployment), osname) == 0)
     {
index d8959c4c50ae256885007c275384f4de6095aa8a..a61f491d1a34a69259a5ccc339b23c918f9db090 100644 (file)
@@ -76,7 +76,6 @@ gboolean ostree_sysroot_deploy_one_tree (OstreeSysroot     *self,
                                          const char        *revision,
                                          GKeyFile          *origin,
                                          char             **add_kernel_argv,
-                                         gboolean           retain,
                                          OstreeDeployment  *provided_merge_deployment,
                                          OstreeDeployment **out_new_deployment,
                                          GCancellable      *cancellable,
index 9761f86460f10906cb9c5c495f2915c8cbf9f098..5e94e90924be4dc40e8b3cc5eef5d76377cc996b 100644 (file)
@@ -54,6 +54,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   gs_unref_object OstreeRepo *repo = NULL;
   gs_unref_ptrarray GPtrArray *new_deployments = NULL;
   gs_unref_object OstreeDeployment *new_deployment = NULL;
+  gs_unref_object OstreeDeployment *merge_deployment = NULL;
   gs_free char *revision = NULL;
 
   context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");
@@ -102,14 +103,33 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
     goto out;
 
+  merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname);
+
+  /* Here we perform cleanup of any leftover data from previous
+   * partial failures.  This avoids having to call gs_shutil_rm_rf()
+   * at random points throughout the process.
+   *
+   * TODO: Add /ostree/transaction file, and only do this cleanup if
+   * we find it.
+   */
+  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+    {
+      g_prefix_error (error, "Performing initial cleanup: ");
+      goto out;
+    }
+
   if (!ostree_sysroot_deploy_one_tree (sysroot,
                                        opt_osname, revision, origin,
-                                       opt_kernel_argv, opt_retain,
-                                       NULL,
+                                       opt_kernel_argv, merge_deployment,
                                        &new_deployment,
                                        cancellable, error))
     goto out;
 
+  if (!ot_admin_complete_deploy_one (sysroot, opt_osname,
+                                     new_deployment, merge_deployment, opt_retain,
+                                     cancellable, error))
+    goto out;
+
   ret = TRUE;
  out:
   if (origin)
index 73b1bf57af6a18ab9f9d418c0c74d1019859c07d..3beb7c927a30f69cc8f723e1beffed28b5e0207a 100644 (file)
@@ -125,14 +125,33 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
   else
     {
       gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/");
+      
+      /* Here we perform cleanup of any leftover data from previous
+       * partial failures.  This avoids having to call gs_shutil_rm_rf()
+       * at random points throughout the process.
+       *
+       * TODO: Add /ostree/transaction file, and only do this cleanup if
+       * we find it.
+       */
+      if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+        {
+          g_prefix_error (error, "Performing initial cleanup: ");
+          goto out;
+        }
+
       if (!ostree_sysroot_deploy_one_tree (sysroot,
                                            opt_osname, new_revision, origin,
-                                           NULL, FALSE,
+                                           NULL,
                                            merge_deployment,
                                            &new_deployment,
                                            cancellable, error))
         goto out;
 
+      if (!ot_admin_complete_deploy_one (sysroot, opt_osname,
+                                         new_deployment, merge_deployment, FALSE,
+                                         cancellable, error))
+        goto out;
+
       if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot))
         {
           gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
index 90f3a4ecb23da6b2a3c4a25e79272404037a6333..3ca7508c0811cd37ed800de4df74a4111f26b103 100644 (file)
@@ -56,3 +56,57 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot       *sysroot,
  out:
   return ret;
 }
+
+gboolean
+ot_admin_complete_deploy_one (OstreeSysroot      *sysroot,
+                              const char         *osname,
+                              OstreeDeployment   *new_deployment,
+                              OstreeDeployment   *merge_deployment,
+                              gboolean            opt_retain,
+                              GCancellable       *cancellable,
+                              GError            **error)
+{
+  gboolean ret = FALSE;
+  guint i;
+  OstreeDeployment *booted_deployment = NULL;
+  gs_unref_ptrarray GPtrArray *deployments = NULL;
+  gs_unref_ptrarray GPtrArray *new_deployments = g_ptr_array_new_with_free_func (g_object_unref);
+
+  deployments = ostree_sysroot_get_deployments (sysroot);
+  booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
+
+  g_ptr_array_add (new_deployments, g_object_ref (new_deployment));
+
+  for (i = 0; i < deployments->len; i++)
+    {
+      OstreeDeployment *deployment = deployments->pdata[i];
+      
+      /* Keep deployments with different osnames, as well as the
+       * booted and merge deployments
+       */
+      if (opt_retain ||
+          strcmp (ostree_deployment_get_osname (deployment), osname) != 0 ||
+          ostree_deployment_equal (deployment, booted_deployment) ||
+          ostree_deployment_equal (deployment, merge_deployment))
+        {
+          g_ptr_array_add (new_deployments, g_object_ref (deployment));
+        }
+      else
+        {
+          g_print ("ostadmin: Will delete deployment osname=%s %s.%u\n",
+                   ostree_deployment_get_osname (deployment),
+                   ostree_deployment_get_csum (deployment),
+                   ostree_deployment_get_deployserial (deployment));
+        }
+    }
+
+  if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable, error))
+    goto out;
+
+  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
index 8b9758d67de16c1436cc6e91868bb8f539ec537c..e0a824d2e98a96286bbd5919ad86053010ec1903 100644 (file)
@@ -35,5 +35,14 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot       *sysroot,
                                               GCancellable        *cancellable,
                                               GError             **error);
 
+gboolean
+ot_admin_complete_deploy_one (OstreeSysroot      *sysroot,
+                              const char         *osname,
+                              OstreeDeployment   *new_deployment,
+                              OstreeDeployment   *merge_deployment,
+                              gboolean            opt_retain,
+                              GCancellable        *cancellable,
+                              GError             **error);
+
 G_END_DECLS
 
index 66139d54aadd6e0321e53c6e2e3fdb8dc0ed80dc..02f5b2085154863b4b6916fc7060912f19ab9888 100755 (executable)
@@ -143,17 +143,12 @@ ostree admin --sysroot=sysroot status
 
 echo "ok upgrade"
 
+assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/etc/os-release 'NAME=TestOS'
 assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS'
-assert_file_has_content sysroot/ostree/deploy/testos/deploy/${origrev}.4/etc/os-release 'NAME=TestOS'
-ostree admin --sysroot=sysroot undeploy 2
+ostree admin --sysroot=sysroot undeploy 1
 assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS'
-assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${origrev}.4
-
-assert_file_has_content sysroot/ostree/deploy/testos/deploy/${origrev}.3/etc/os-release 'NAME=TestOS'
-ostree admin --sysroot=sysroot undeploy 2
-assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${origrev}.3
+assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${rev}.0
 
-assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS'
 ostree admin --sysroot=sysroot undeploy 0
 assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${newrev}.0
 ostree admin --sysroot=sysroot status